#pragma once

#include "xict_common/log/log.h"
#include <chrono>
#include <cmath>
#include <string>
#include <unordered_map>
#include <vector>

namespace xict_common {
    namespace utils {

        /// \brief A timer class for measuring elapsed time.
        class Timer {
        public:
            /// \brief Constructs a new Timer object.
            Timer() = default;

            /// \brief Starts the performance timer.
            /// \details This function starts the performance timer, which
            /// can be used to measure the execution time of a block of
            /// code. Call End() to stop the timer and get the elapsed time.
            /// \note This function is not thread-safe.
            void Start();

            /// \brief Stops the performance timer and returns the elapsed
            /// time. \details This function stops the performance timer and
            /// returns the elapsed time in milliseconds. It also outputs
            /// the stdout with a prefix "TIMER". It will automatically
            /// start a new timer. \param msg The message prefix to output.
            /// \return The elapsed time in milliseconds.
            /// \note This function is not thread-safe.
            uint64_t End(const std::string& msg);

            /// \brief Deleted copy constructor.
            Timer(const Timer&) = delete;
            /// \brief Deleted copy assignment operator.
            Timer& operator=(const Timer&) = delete;

        private:
            // in ms.
            // uint64_t start_time_;
            // uint64_t end_time_;

            /// \brief The start time of the timer in nanoseconds.
            std::chrono::time_point<std::chrono::high_resolution_clock>
                start_time_;
            /// \brief The end time of the timer in nanoseconds.
            std::chrono::time_point<std::chrono::high_resolution_clock>
                end_time_;
        };

        /// \brief A wrapper class for Timer.
        class TimerWrapper {
        public:
            /// \brief Constructs a new TimerWrapper object.
            /// \param msg The message prefix to output.
            explicit TimerWrapper(std::string msg);

            /// \brief Deconstructs the TimerWrapper object.
            ~TimerWrapper();

            /// \brief Deleted copy constructor.
            TimerWrapper(const TimerWrapper&) = delete;
            /// \brief Deleted copy assignment operator.
            TimerWrapper& operator=(const TimerWrapper&) = delete;

        private:
            /// \brief The timer object.
            Timer timer_;
            /// \brief The message prefix to output.
            std::string msg_;
        };

        class PerformanceStats {
        public:
            /// \brief Adds a time record.
            /// \param msg The message prefix to output.
            /// \param time The elapsed time in milliseconds.
            static void AddTime(const std::string& msg, uint64_t time);
            /// \brief Gets the average time.
            /// \param msg The message prefix to output.
            /// \return The average time in milliseconds.
            static double GetAverageTime(const std::string& msg);
            /// \brief Prints the statistics.
            /// \param msg The message prefix to output.
            static void PrintStats(const std::string& msg);

        private:
            /// \brief The time records.
            /// \details This is a map from the message prefix to a vector
            /// of elapsed times in milliseconds.
            static std::unordered_map<std::string, std::vector<uint64_t>>
                time_records_;
        };

        /// \brief Gets the full name of a function.
        /// \details This function gets the full name of a function. The
        /// full name includes name within the '(' and ')' characters.
        /// \param full_name The full name of the function.
        /// \return The full name of the function.
        std::string get_full_name(const std::string& full_name);

        /// \brief Gets the full name of a function with an indicator.
        /// \param full_name The full name of the function.
        /// \param indicator The indicator to add to the full name.
        /// \return The full name of the function with the indicator.
        std::string get_full_name(const std::string& full_name,
                                  const std::string& indicator);

#ifdef ZETTON_DISABLE_PERF

// disable macros.
#define ZETTON_PERF_FUNCTION()

#define ZETTON_PERF_FUNCTION_WITH_INDICATOR(indicator)

#define ZETTON_PERF_BLOCK_START()

#define ZETTON_PERF_BLOCK_END(msg)

#define ZETTON_PERF_BLOCK_END_WITH_INDICATOR(indicator, msg)

#define ZETTON_PERF_BLOCK_END_WITH_STATS(msg)

#define ZETTON_PERF_BLOCK_END_WITH_STATS_AND_INDICATOR(indicator, msg)

#else

/// \brief Macro to initialize a timer and start it.
#define ZETTON_PERF_FUNCTION()                          \
    ::xict_common::utils::TimerWrapper _timer_wrapper_( \
        ::xict_common::utils::get_full_name(__PRETTY_FUNCTION__))

/// \brief Macro to initialize a timer and start it with an indicator.
#define ZETTON_PERF_FUNCTION_WITH_INDICATOR(indicator) \
    svaf::common::util::TimerWrapper _timer_wrapper_(  \
        ::xict_common::utils::get_full_name(__PRETTY_FUNCTION__, indicator))

/// \brief Macro to start a timer.
#define ZETTON_PERF_BLOCK_START()        \
    ::xict_common::utils::Timer _timer_; \
    _timer_.Start()

/// \brief Macro to stop a timer and output the elapsed time.
#define ZETTON_PERF_BLOCK_END(msg) _timer_.End(msg)

/// \brief Macro to stop a timer and output the elapsed time with an indicator.
#define ZETTON_PERF_BLOCK_END_WITH_INDICATOR(indicator, msg) \
    _timer_.End((indicator) + "_" + (msg))

/// \brief Macro to stop a timer and output the elapsed time and performance
/// statistics.
#define ZETTON_PERF_BLOCK_END_WITH_STATS(msg)                               \
    {                                                                       \
        auto elapsed_time = _timer_.End(msg);                               \
        ::xict_common::utils::PerformanceStats::AddTime(msg, elapsed_time); \
        ::xict_common::utils::PerformanceStats::PrintStats(msg);            \
    }

/// \brief Macro to stop a timer and output the elapsed time and performance
/// statistics with an indicator.
#define ZETTON_PERF_BLOCK_END_WITH_STATS_AND_INDICATOR(indicator, msg)         \
    {                                                                          \
        auto elapsed_time = _timer_.End((indicator) + "_" + (msg));            \
        ::xict_common::utils::PerformanceStats::AddTime(                       \
            (indicator) + "_" + (msg), elapsed_time);                          \
        ::xict_common::utils::PerformanceStats::PrintStats((indicator) + "_" + \
                                                           (msg));             \
    }

#endif   // ZETTON_DISABLE_PERF

    }   // namespace utils
}   // namespace xict_common
